Skip to content

docs: expand RequestOptions.vf() with vf_ wire-format rules - #1840

Open
jacalata wants to merge 5 commits into
developmentfrom
jac/vf-docs
Open

docs: expand RequestOptions.vf() with vf_ wire-format rules#1840
jacalata wants to merge 5 commits into
developmentfrom
jac/vf-docs

Conversation

@jacalata

Copy link
Copy Markdown
Contributor

Summary

The vf_ REST view filter has several server-side quirks that aren't obvious from the current one-line vf() docstring. This expands it to document what values actually mean on the wire: exact-match by default, , as OR-list, \, for a literal comma, the empty-value override behavior, no wildcards, no operators, boolean 'true'/'false' only.

Callers who build vf_ requests through TSC no longer have to reverse-engineer server behavior to figure out why their filter returned zero rows.

Why now

Working through --filter parsing bugs in tabcmd, I verified the wire-format behavior end-to-end against Tableau Cloud. Recording those findings in the library the tabcmd (and other callers) build on top of, so the next person doesn't have to redo the experiment.

Notable server-side behaviors documented:

  • Rock\, Paper\, Scissors — backslash-escape works for a literal comma. %2C does NOT.
  • vf_Region= (empty value) — overrides any workbook-embedded filter on that column. Undocumented but stable and useful.
  • * — NOT a wildcard on vf_. It's a wildcard on the filter= list-endpoint syntax, which is a different query parameter.

Test plan

  • help(PDFRequestOptions.vf) renders the expanded docstring cleanly
  • mypy passes (pre-commit hook)
  • No behavior change; docstring-only

🤖 Generated with Claude Code

jacalata and others added 2 commits July 29, 2026 15:57
The vf_ view filter has several server-side quirks that aren't obvious
from the current one-line docstring. Document what values actually mean
on the wire: exact match by default, comma as OR-list, backslash-escape
for literal comma, empty-value override behavior, no wildcards, no
operators, boolean 'true'/'false' only.

This surfaces the behavior verified end-to-end against Tableau Cloud
while working through --filter parsing issues in tabcmd, so callers who
build vf_ requests through TSC know what the server will accept without
having to reverse-engineer it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `filter=` list-endpoint syntax is unrelated to view-filter export
requests (it's for querying lists of workbooks/users/etc). Steering
readers there was scope creep. Just say ranges/operators aren't
supported and point at workbook-side design.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown

Coverage

Coverage Report
FileStmtsMissCoverMissing
tableauserverclient
   __init__.py50100% 
   config.py150100% 
   datetime_helpers.py2511 96%
   exponential_backoff.py200100% 
   filesys_helpers.py310100% 
   namespace.py2633 88%
tableauserverclient/bin
   __init__.py20100% 
   _version.py358212212 41%
tableauserverclient/helpers
   __init__.py10100% 
   logging.py20100% 
   strings.py3111 97%
tableauserverclient/models
   __init__.py460100% 
   collection_item.py4177 83%
   column_item.py553232 42%
   connection_credentials.py351111 69%
   connection_item.py941414 85%
   custom_view_item.py1442121 85%
   data_acceleration_report_item.py5411 98%
   data_alert_item.py15844 97%
   data_freshness_policy_item.py1551515 90%
   database_item.py2073636 83%
   datasource_item.py3001212 96%
   dqw_item.py10455 95%
   exceptions.py40100% 
   extensions_item.py13244 97%
   extract_item.py4444 91%
   favorites_item.py6988 88%
   fileupload_item.py190100% 
   flow_item.py1491010 93%
   flow_run_item.py710100% 
   group_item.py8966 93%
   groupset_item.py4977 86%
   interval_item.py1823232 82%
   job_item.py1871010 95%
   linked_tasks_item.py7911 99%
   location_item.py2922 93%
   metric_item.py1291313 90%
   oidc_item.py6333 95%
   pagination_item.py3411 97%
   permissions_item.py1111212 89%
   project_item.py2073131 85%
   property_decorators.py1001818 82%
   reference_item.py2622 92%
   revision_item.py5911 98%
   schedule_item.py20966 97%
   server_info_item.py3777 81%
   site_item.py6361313 98%
   subscription_item.py10122 98%
   table_item.py1191818 85%
   tableau_auth.py612525 59%
   tableau_types.py2711 96%
   tag_item.py150100% 
   target.py60100% 
   task_item.py5622 96%
   user_item.py3101818 94%
   view_item.py2201616 93%
   virtual_connection_item.py6488 88%
   webhook_item.py6911 99%
   workbook_item.py3621616 96%
tableauserverclient/server
   __init__.py90100% 
   exceptions.py40100% 
   filter.py2911 97%
   pager.py3311 97%
   query.py1431515 90%
   request_factory.py1335195195 85%
   request_options.py38655 99%
   server.py1882323 88%
   sort.py60100% 
tableauserverclient/server/endpoint
   __init__.py350100% 
   auth_endpoint.py771111 86%
   custom_views_endpoint.py1521212 92%
   data_acceleration_report_endpoint.py210100% 
   data_alert_endpoint.py942323 76%
   databases_endpoint.py1113030 73%
   datasources_endpoint.py3233333 90%
   default_permissions_endpoint.py4433 93%
   dqw_endpoint.py451616 64%
   endpoint.py2122020 91%
   exceptions.py7766 92%
   extensions_endpoint.py310100% 
   favorites_endpoint.py942222 77%
   fileuploads_endpoint.py510100% 
   flow_runs_endpoint.py6299 85%
   flow_task_endpoint.py2122 90%
   flows_endpoint.py1985353 73%
   groups_endpoint.py12699 93%
   groupsets_endpoint.py7277 90%
   jobs_endpoint.py6799 87%
   linked_tasks_endpoint.py370100% 
   metadata_endpoint.py881414 84%
   metrics_endpoint.py5566 89%
   oidc_endpoint.py4211 98%
   permissions_endpoint.py4433 93%
   projects_endpoint.py1782424 87%
   resource_tagger.py1273535 72%
   schedules_endpoint.py1191111 91%
   server_info_endpoint.py361010 72%
   sites_endpoint.py1302727 79%
   subscriptions_endpoint.py561414 75%
   tables_endpoint.py1103636 67%
   tasks_endpoint.py6366 90%
   users_endpoint.py18388 96%
   views_endpoint.py15099 94%
   virtual_connections_endpoint.py1131010 91%
   webhooks_endpoint.py5499 83%
   workbooks_endpoint.py3382222 93%
TOTAL12002142388% 

jacalata and others added 3 commits July 29, 2026 16:00
Empirically verified: '\' in a vf_ value is the escape character. To
match a literal backslash you must double it ('\\'). Consolidate the
comma-escape and backslash rules into a single "backslash escapes"
bullet, since it's one mechanism.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Dropped the abstract "\c means literal c" phrasing (weird to read with
a random letter) and led with the two concrete cases -- escape a comma,
escape a backslash -- and noted why each escape matters (comma would
otherwise start an OR-list; backslash otherwise consumed as escape).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Empirically tested 20+ candidate metacharacters (/ % + : ; { } [ ] ( )
? # * @ = & " ' < >) against a live server; every one passes through
untouched when URL-encoded. Only ',' and '\' are vf_ metacharacters
that need escaping in the wire value. Explicitly call that out so
readers don't assume everything URL-special needs a client-side
workaround.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant